package com.joshjung.collections
{
    import flash.errors.IllegalOperationError;
    import flash.events.EventDispatcher;
    import flash.utils.Dictionary;

    /**
     *
     * @author jjung
     */
    public class LinkedList extends EventDispatcher
    {
        //---------------------------------------------------------------------
        //
        //  Variables
        //
        //---------------------------------------------------------------------
        protected var _head : LLElement;
        protected var _tail : LLElement;

        protected var dataToElement : Dictionary = new Dictionary(true);
        protected var idToElement : Dictionary = new Dictionary(true);

        //---------------------------------------------------------------------
        //
        //  Properties
        //
        //---------------------------------------------------------------------
        public function get head() : LLElement
        {
            return _head;
        }

        public function get tail() : LLElement
        {
            return _tail;
        }

        //---------------------------------------------------------------------
        //
        //  Constructor
        //
        //---------------------------------------------------------------------
        /**
         *  Constructor
         */
        public function LinkedList()
        {
        }

        //---------------------------------------------------------------------
        //
        //  Methods
        //
        //---------------------------------------------------------------------
        public function clear() : void
        {
            _head = _tail = null;
            dataToElement = new Dictionary(true);
            idToElement = new Dictionary(true);
        }

        public function find(id : *, property : String = null) : *
        {
            var iterator : LLIterator = new LLIterator(this);
            var data : *;

            while (data = iterator.next())
            {
                if (property)
                {
                    if (data.hasOwnProperty(property) && data[property] == id)
                    {
                        return data;
                    }
                }
                else if (data is ILLIdentifiable && (data as ILLIdentifiable).id == id)
                {
                    return data;
                }
            }

            return null;
        }

        public function insert(data : *) : void
        {
            if (dataToElement[data] !== undefined)
            {
                return;
            }

            if (!head)
            {
                _head = _tail = new LLElement(data, null, null);
                dataToElement[data] = _head;
            }
            else
            {
                _head.next = new LLElement(data, _head, null);
                dataToElement[data] = _head.next;
                _head = _head.next;
            }

            if (data is ILLIdentifiable)
            {
                idToElement[(data as ILLIdentifiable).id] = _head;
            }

            dispatchEvent(new LLEvent(LLEvent.INSERT, data));
        }

        public function remove(data : *) : void
        {
            var elem : LLElement = dataToElement[data];

            if (elem)
            {
                if (elem == _head)
                {
                    _head = elem.prev;
                }
                if (elem == _tail)
                {
                    _tail = elem.next;
                }

                if (elem.prev)
                    elem.prev.next = elem.next;

                if (elem.next)
                    elem.next.prev = elem.prev;

                if (elem is ILLIdentifiable)
                {
                    delete idToElement[(elem as ILLIdentifiable).id];
                }

                delete dataToElement[data];

                dispatchEvent(new LLEvent(LLEvent.REMOVE, data));
            }
        }
    }
}
